#pragma once
//#include "stdafx.h"
#include "Space.h"
#include "SpaceNode.h"
#include "DataLayer.h"
#include <map>
#include <string>
using namespace std;
namespace org
{
	namespace spark
	{
		namespace space
		{
			//using org::spark::core::ExecutionMode;
			using org::spark::core::Observer;
			using org::spark::data::DataLayer;
			using org::spark::data::Grid;
			using org::spark::math::Vector;

			Space::Space() : executionMode(observer->getExecutionMode())
			{
				//InitializeInstanceFields();
				observer = Observer::getInstance();


				nodeQueue = std::vector<SpaceNode*>(1000);
				//dataLayers std::map<std::string, DataLayer*>();
				//dataLayersList = std::vector<DataLayer*>();
			}

			void Space::setIndex(int index)
			{
				this->spaceIndex = index;
			}

			int Space::getIndex()
			{
				return spaceIndex;
			}

// There is no native C++ template equivalent to generic constraints:
template<typename T>
			T Space::addDataLayer(const std::string &name, T data)
			{
				if (data->getSpace() != this)
					////throw Error("Cannot add data layer " + name + " into space " + this);

				dataLayers[name] = data;
				dataLayersList.push_back(data);

				return data;
			}

			DataLayer *Space::getDataLayer(const std::string &name)
			{
				 std::map<std::string, DataLayer*>::iterator p;
				 p=dataLayers.find(name);
				 DataLayer *Dl=p->second;
				 return Dl;
			}

			std::string *Space::getDataLayerNames()
			{
				std::string tmp[dataLayers.size()];
				tmp = dataLayers.keySet()->toArray(tmp);

				return tmp;
			}

			void Space::processAllDataLayers(long long tick)
			{
				for (int i = 0; i < dataLayersList.size(); i++)
				{
					dataLayersList[i]->process(tick);
				}
			}

			void Space::processNodes()
			{
				for (std::vector<SpaceNode*>::const_iterator node = nodeQueue.begin(); node != nodeQueue.end(); ++node)
				{
					if (((*node)->state & SpaceNode::NODE_IS_REMOVING) != 0)
					{
						removeNode0(*node);
						continue;
					}

					if (((*node)->state & SpaceNode::NODE_IS_CREATING) != 0)
					{
						if (((*node)->state & SpaceNode::NODE_IS_MOVING) != 0)
						{
							addNodeAndMove0(*node);
							(*node)->newPosition->set((*node)->position);
						}
						else
						{
							addNode0(*node);
						}
					}
					else
					{
						if (((*node)->state & SpaceNode::NODE_IS_MOVING) != 0)
						{
							changeNodePosition0(*node);
							(*node)->newPosition->set((*node)->position);
						}
					}

					(*node)->state = 0;
				}

				nodeQueue.clear();
				postProcess();
			}

			void Space::postProcess()
			{

			}

			CircleNode *Space::createCircleNode(double r, SpaceAgent *agent, int type)
			{
				if (r > maximumNodeRadius)
					maximumNodeRadius = r;

				CircleNode *node = createCircleNode0(r, type);
				node->agent = agent;
				node->next = node->prev = node;


				// observer.isSerial() is always true during the setup process
				if (observer->isSerial())
				{
					addNode0(node);
					return node;
				}

				/*
				switch (executionMode)
				{
				case ExecutionMode::CONCURRENT_MODE:
					if (node->state == 0)
					{
						nodeQueue.push_back(node);
					}
					node->state |= SpaceNode::NODE_IS_CREATING;
					break;

				case ExecutionMode::PARALLEL_MODE:
// There is no built-in support for multithreading in native C++:
					synchronized(nodeQueue)
					{
						if (node->state == 0)
						{
							nodeQueue.push_back(node);
						}
						node->state |= SpaceNode::NODE_IS_CREATING;
					}
					break;

				default:
					//throw Error("Unexpected execution mode");

				}
				*/
				return node;
			}

			CircleNode *Space::createCircleNode0(double r, int type)
			{
				CircleNode *node = new CircleNode(this, r);
				return node;
			}

			SquareNode *Space::createSquareNode(double r, SpaceAgent *agent)
			{
				if (r > maximumNodeRadius)
					maximumNodeRadius = r;

				SquareNode *node = createSquareNode0(r);
				node->agent = agent;
				node->next = node->prev = node;

				if (observer->isSerial())
				{
					addNode0(node);
					return node;
				}

				switch (executionMode)
				{
				case ExecutionMode::CONCURRENT_MODE:
					if (node->state == 0)
					{
						nodeQueue.push_back(node);
					}
					node->state |= SpaceNode::NODE_IS_CREATING;
					break;

				case ExecutionMode::PARALLEL_MODE:
// There is no built-in support for multithreading in native C++:
					synchronized(nodeQueue)
					{
						if (node->state == 0)
						{
							nodeQueue.push_back(node);
						}
						node->state |= SpaceNode::NODE_IS_CREATING;
					}
					break;

				default:
					//throw Error("Unexpected execution mode");
				}

				return node;
			}

			SquareNode *Space::createSquareNode0(double r)
			{
				SquareNode *node = new SquareNode(this, r);
				return node;
			}

			/*Square2Node *Space::createSquare2Node(double r, SpaceAgent *agent)
			{
				if (r > maximumNodeRadius)
					maximumNodeRadius = r;

				Square2Node *node = createSquare2Node0(r);
				node->agent = agent;
				node->next = node->prev = node;

				if (observer->isSerial())
				{
					addNode0(node);
					return node;
				}

				switch (executionMode)
				{
				case ExecutionMode::CONCURRENT_MODE:
					if (node->state == 0)
					{
						nodeQueue.push_back(node);
					}
					node->state |= SpaceNode::NODE_IS_CREATING;
					break;

				case ExecutionMode::PARALLEL_MODE:
// There is no built-in support for multithreading in native C++:
					synchronized(nodeQueue)
					{
						if (node->state == 0)
						{
							nodeQueue.push_back(node);
						}
						node->state |= SpaceNode::NODE_IS_CREATING;
					}
					break;

				default:
					//throw Error("Unexpected execution mode");
				}

				return node;
			}

			Square2Node *Space::createSquare2Node0(double r)
			{
				Square2Node *node = new Square2Node(this, r);
				return node;
			}*/

			void Space::removeNode(SpaceNode *node)
			{
				if (node->nodeIsRemoved)
					return;

				node->nodeIsRemoved = true;

				if (observer->isSerial())
				{
					removeNode0(node);
					return;
				}

				switch (executionMode)
				{
				case ExecutionMode::CONCURRENT_MODE:
					if (node->state == 0)
					{
						nodeQueue.push_back(node);
					}
					node->state |= SpaceNode::NODE_IS_REMOVING;
					break;

				case ExecutionMode::PARALLEL_MODE:
// There is no built-in support for multithreading in native C++:
					synchronized(nodeQueue)
					{
						if (node->state == 0)
						{
							nodeQueue.push_back(node);
						}
						node->state |= SpaceNode::NODE_IS_REMOVING;
					}
					break;

				default:
					//throw Error("Unexpected execution mode");
				}
			}

			void Space::changeNodePosition(SpaceNode *node)
			{
				if (observer->isSerial())
				{
					changeNodePosition0(node);
					node->newPosition->set(node->position);
					return;
				}

				

					//throw Error("Unexpected execution mode");

				}

			}

			std::vector<SpaceAgent*> Space::getAgents(Vector *v, double radius)
			{
				if (executionMode == ExecutionMode::SERIAL_MODE)
				{
					temp->setRadius0(radius);
					temp->position->set(v);
					return getAgents(temp);
				}
				else
				{
					CircleNode *temp = new CircleNode(this, radius);
					temp->position->set(v);
					return getAgents(temp);
				}
			}

			std::vector<SpaceAgent*> Space::getAgents(SpaceAgent *agent)
			{
				return getAgents(agent->node);
			}

// There is no native C++ template equivalent to generic constraints:
template<typename T> where T : SpaceAgent
			std::vector<T> Space::getAgents(Vector *v, double radius, Class *type)
			{
				if (executionMode == ExecutionMode::SERIAL_MODE)
				{
					temp->setRadius0(radius);
					temp->position->set(v);
					return getAgents(temp, type);
				}
				else
				{
					CircleNode *temp = new CircleNode(this, radius);
					temp->position->set(v);
					return getAgents(temp, type);
				}
			}

// There is no native C++ template equivalent to generic constraints:
template<typename T> where T : SpaceAgent
			std::vector<T> Space::getAgentsOfKind(Vector *v, double radius, Class *type)
			{
				if (executionMode == ExecutionMode::SERIAL_MODE)
				{
					temp->setRadius0(radius);
					temp->position->set(v);
					return getAgentsOfKind(temp, type);
				}
				else
				{
					CircleNode *temp = new CircleNode(this, radius);
					temp->position->set(v);
					return getAgentsOfKind(temp, type);
				}
			}

// There is no native C++ template equivalent to generic constraints:
template<typename T> where T : SpaceAgent
			std::vector<T> Space::getAgents(SpaceAgent *agent, Class *type)
			{
				return getAgents(agent->node, type);
			}

// There is no native C++ template equivalent to generic constraints:
template<typename T> where T : SpaceAgent
			std::vector<T> Space::getAgentsOfKind(SpaceAgent *agent, Class *type)
			{
				return getAgentsOfKind(agent->node, type);
			}

			org::spark::math::Vector *Space::getVector(SpaceAgent *agent1, SpaceAgent *agent2)
			{
				return getVector(agent1->node->position, agent2->node->position);
			}

			void Space::dataLayersBeginStep()
			{
				for (std::vector<DataLayer*>::const_iterator data = dataLayersList.begin(); data != dataLayersList.end(); ++data)
				{
					(*data)->beginStep();
				}
			}

			void Space::dataLayersEndStep()
			{
				for (std::vector<DataLayer*>::const_iterator data = dataLayersList.begin(); data != dataLayersList.end(); ++data)
				{
					(*data)->endStep();
				}
			}

			void Space::serializeSpace(ObjectOutputStream *oos) //throw(Exception)
			{
				oos->writeObject(this);
			}

			org::spark::space::Space *Space::loadSpace(ObjectInputStream *ois) //throw(Exception)
			{
				return static_cast<Space*>(ois->readObject());
			}

			std::string Space::ToString()
			{
				return observer->getSpaceName(this);
			}

			void Space::readObject(ObjectInputStream *ois) //throw(IOException, ClassNotFoundException)
			{

				ois->defaultReadObject();

				nodeQueue = std::vector<SpaceNode*>(1000);

				// Load data layers
				dataLayers = std::map<std::string, DataLayer*>();
				dataLayersList = std::vector<DataLayer*>();

				int n = ois->readInt();
				for (int i = 0; i < n; i++)
				{
					std::string name = static_cast<std::string>(ois->readObject());
					DataLayer *data = static_cast<DataLayer*>(ois->readObject());

					if (dynamic_cast<Grid*>(data) != 0)
					{
						(static_cast<Grid*>(data))->setSpace(this);
					}
					else
					{
						//throw Error("Unsupported data layer type");
					}

					addDataLayer(name, data);
				}
			}

			void Space::writeObject(ObjectOutputStream *oos) //throw(IOException)
			{
				oos->defaultWriteObject();

				oos->writeInt(dataLayersList.size());
				for (std::map<std::string, DataLayer*>::const_iterator name = dataLayers.begin(); name != dataLayers.end(); ++name)
				{
					DataLayer *data = dataLayers[name->first];

					oos->writeObject(name->first);
					oos->writeObject(data);
				}
			}

			void Space::InitializeInstanceFields()
			{
				temp = new CircleNode(this, 0.0);
			}
		}
	}
}
